//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft end-user
// license agreement (EULA) under which you licensed this SOFTWARE PRODUCT.
// If you did not accept the terms of the EULA, you are not authorized to use
// this source code. For a copy of the EULA, please see the LICENSE.RTF on your
// install media.
//
#include <windows.h>
#include <bsp.h>
#include "loader.h"
#include <fmd.h>

// Number of beginning SmartMedia (NAND) blocks to mark as reserved.
//
#define NAND_START_RESERVED_BLOCKS      3

BOOL g_bSmartMediaExist = FALSE;		// Is there a SmartMedia card on this device?


// Define a dummy SetKMode function to satisfy the NAND FMD.
// Can not remove it.
DWORD SetKMode (DWORD fMode)
{
    return(1);
}


/*
    @func   BOOL | WriteImageToSmartMedia | Stores the image cached in RAM to the Smart Media card.  We assume that FMD_Init has already been called at this point.
    @rdesc  TRUE = Success, FALSE = Failure.
    @comm    
    @xref   
*/
BOOL WriteDiskImageToSmartMedia(DWORD dwImageStart, DWORD dwImageLength, BOOT_CFG *pBootCfg)
{
    FlashInfo NandFlashInfo;
    BYTE *pBuffer;
    UINT32 NeedBlocks;
    UINT32 SectorCnt;
    UINT32 BlockId;
    UINT32 SectorId;
    BOOL WrErr = FALSE;
    BOOL WriteEnd = FALSE;
    DWORD length;
    DWORD LengthResume;
    BOOL status;
    SectorInfo SI = {0xffffffff, OEM_BLOCK_RESERVED, 0xff, 0xffff};
        
    EdbgOutputDebugString("Write image to SmartMedia\r\n");
    EdbgOutputDebugString("ram addr : 0x%x, \r\n", dwImageStart);
    EdbgOutputDebugString("length   : 0x%x, \r\n", dwImageLength);
    
    FMD_GetInfo(&NandFlashInfo);
    EdbgOutputDebugString("flashType = %d\r\n", NandFlashInfo.flashType);
    EdbgOutputDebugString("dwNumBlocks = %d\r\n", NandFlashInfo.dwNumBlocks);
    EdbgOutputDebugString("dwBytesPerBlock = %d\r\n", NandFlashInfo.dwBytesPerBlock);
    EdbgOutputDebugString("wSectorsPerBlock = %d\r\n", NandFlashInfo.wSectorsPerBlock);
    EdbgOutputDebugString("wDataBytesPerSector = %d\r\n", NandFlashInfo.wDataBytesPerSector);
    
    // If there isn't a SmartMedia card in the system, it'll be kind of hard to write an image to it...
    //
    if (!g_bSmartMediaExist)
    {
        EdbgOutputDebugString("WARNING: Smart Media device doesn't exist - unable to store image.\r\n");
        return(FALSE);
    }
    
    NeedBlocks = dwImageLength / NandFlashInfo.dwBytesPerBlock;
    if((dwImageLength % NandFlashInfo.dwBytesPerBlock) != 0)
    {
        NeedBlocks++;
    }
    //todo: is out of block range of this partition?
    //if(out of range)
    //{
    //    return (FALSE);
    //}
    
    EdbgOutputDebugString("need %d block!\r\n", NeedBlocks);
    
    BlockId = pBootCfg->NandImageAddress / NandFlashInfo.dwBytesPerBlock;
    length = dwImageLength;
    pBuffer = (UINT8 *)dwImageStart;
    while(!WriteEnd)
    {
        WrErr = FALSE;
        
        //1. is this block back?
        if(FMD_GetBlockStatus(BlockId) & BLOCK_STATUS_BAD)
        {
            EdbgOutputDebugString("Bad Block %d!\r\n", BlockId);
            BlockId++;
            continue;
        }
        
        //2. is out of block range of this partition?
        //if(out of range)
        //{
        //    return (FALSE);
        //}
        
        //3. erase block
        EdbgOutputDebugString("erasing block %d...\r\n", BlockId);
        if(!FMD_EraseBlock(BlockId))
        {
            EdbgOutputDebugString("Erase err:Bad Block %d!\r\n", BlockId);
            FMD_SetBlockStatus(BlockId, BLOCK_STATUS_BAD);
            break;
        }
        
        //4. write block
        SectorId = BlockId * NandFlashInfo.wSectorsPerBlock;
        LengthResume = length;
        EdbgOutputDebugString("Writing");
        
        for(SectorCnt = 0; SectorCnt < NandFlashInfo.wSectorsPerBlock; SectorCnt++)
        {
            status = FMD_WriteSector(SectorId, (pBuffer + dwImageLength - length), &SI, 1);
            if(!status)
            {
                EdbgOutputDebugString("Write err: Sector: %d; Bad Block %d!\r\n", SectorId, BlockId);
                WrErr = TRUE;     //this is
                FMD_SetBlockStatus(BlockId, BLOCK_STATUS_BAD);
                length = LengthResume;
                break;
            }
            EdbgOutputDebugString(".");
            SectorId++;
            if(length >= NandFlashInfo.wDataBytesPerSector)
            {
                length -= NandFlashInfo.wDataBytesPerSector;
            }
            else
            {
                length -= length;
                WriteEnd = TRUE;
                break;
            }
        }
        EdbgOutputDebugString("\r\n");
        BlockId++;
    }
    return(TRUE);

}


/*
    @func   BOOLEAN | FormatSmartMedia | Performs a low-level format on the SmartMedia card.
    @rdesc  TRUE == Success and FALSE == Failure.
    @comm    
    @xref   
*/
BOOL FormatSmartMedia(void)
{
#if 0
    UINT32 ulBlockNumber;
    FlashInfo FlashInfo;

    // Get NAND flash data bytes per sector.
    //
    if (!FMD_GetInfo(&FlashInfo))
    {
        EdbgOutputDebugString("ERROR: Unable to get SmartMedia flash information.\r\n");
        return(FALSE);
    }

    for (ulBlockNumber = 0 ; ulBlockNumber < FlashInfo.dwNumBlocks ; ulBlockNumber++)
    {
        // Is the block bad?
        //
        if (FMD_GetBlockStatus(ulBlockNumber) == BLOCK_STATUS_BAD)
        {
            EdbgOutputDebugString("INFO: Found bad SmartMedia block [0x%x].\r\n", ulBlockNumber);
            continue;
        }

        // Erase the block...
        //
        if (!FMD_EraseBlock(ulBlockNumber))
        {
            EdbgOutputDebugString("ERROR: Unable to erase SmartMedia block 0x%x.\r\n", ulBlockNumber);
            return(FALSE);
        }
    }
#endif
    return(TRUE);
}

BOOL ReadDiskImageFromSmartMedia(BOOT_CFG *pBootCfg)
{
    FlashInfo NandFlashInfo;
    BYTE *pBuffer;

    UINT32 SectorCnt;
    UINT32 BlockId;
    UINT32 SectorId;
    BOOL WrErr = FALSE;
    BOOL ReadEnd = FALSE;
    DWORD length;
    BOOL status;
    ULONG ImageLength;
    SectorInfo SI;
    
    EdbgOutputDebugString("Read image From SmartMedia\r\n");    
    FMD_GetInfo(&NandFlashInfo);
    
    // If there isn't a SmartMedia card in the system, it'll be kind of hard to write an image to it...
    //
    if (!g_bSmartMediaExist)
    {
        EdbgOutputDebugString("WARNING: Smart Media device doesn't exist - unable to store image.\r\n");
        return(FALSE);
    }
    
    ImageLength = pBootCfg->NandImageLength;
    
    BlockId = pBootCfg->NandImageAddress / NandFlashInfo.dwBytesPerBlock;
    EdbgOutputDebugString("Nand address = 0x%x(block %d). length = %d bytes\r\n", pBootCfg->NandImageAddress, BlockId, ImageLength);
    EdbgOutputDebugString("Lunch address = 0x%x.\r\n", pBootCfg->LaunchAddress);     

    pBuffer = (BYTE *)pBootCfg->LaunchAddress;
    length = ImageLength;
    EdbgOutputDebugString("Loading");
    while(!ReadEnd)
    {
        //1. is this block back?
        if(FMD_GetBlockStatus(BlockId) & BLOCK_STATUS_BAD)
        {
            EdbgOutputDebugString("\r\nRead Error: Bad Block %d!\r\n", BlockId);
            BlockId++;
            continue;
        }
        
        //2. is out of block range?
        
        //3. Read block
        SectorId = BlockId * NandFlashInfo.wSectorsPerBlock;
        EdbgOutputDebugString(">");
        for(SectorCnt = 0; SectorCnt < NandFlashInfo.wSectorsPerBlock; SectorCnt++)
        {
            status = FMD_ReadSector(SectorId, (pBuffer + ImageLength - length), &SI, 1);
            if(!status)
            {
                EdbgOutputDebugString("\r\nFaild @b%d:s%d\r\n", BlockId, SectorId);
                return FALSE;
            }
            SectorId++;
            if(length >= NandFlashInfo.wDataBytesPerSector)
            {
                length -= NandFlashInfo.wDataBytesPerSector;
            }
            else
            {
                length -= length;
                ReadEnd = TRUE;
                break;
            }
        }
        BlockId++;
    }
    EdbgOutputDebugString("\r\nDone!\r\n");
    return(TRUE);
}
